/*
2015-12-5
学习LCD1602屏幕的控制
*/

#include "../lib/inc/stm8s.h"
#include "../lib/inc/stm8s_gpio.h"
#include "../lib/inc/stm8s_clk.h" 

/* 
STM8的银杏学习板，1602的引脚的接法
RS --- PD6
RW --- PD5
EN --- PD4
DB01 --- PD3
DB02 --- PD2
DB03 --- PC6
DB04 --- PC5
DB05 --- PC4
DB06 --- PC3
DB07 --- PB4
DB08 --- PB5
*/

#define Lcd_Port				GPIOD
#define Lcd_Rs					GPIO_PIN_6
#define Lcd_Rw					GPIO_PIN_5
#define Lcd_En					GPIO_PIN_4

#define Lcd_Clear				0x01//LCD清屏命令
#define Lcd_Close_Cursor		0x0C//光标关闭

/******************************************/

typedef enum
{
	CMD = 0, //命令
	DATA=!CMD//数据
}
Data_Flag; //表达MCU要给LCD发送的是数据还是指令

ErrorStatus CLK_Configuration(void); //切换到HSE时钟
void GPIO_Configuration(void); //端口配置
void DelayUS(u8 count); //延时
void DelayMS(u16 count);
void Lcd_Configuration(); //LCD初始化
void Set_Lcd_Postion(u8 X, u8 Y); //光标位置
void Lcd_Write_Data(Data_Flag Flag, u8 data); //LCD发送数据
void Lcd_Write_Port_Data(u8 data);
unsigned char* tr_4(unsigned int outData4);

//大约1ms  每增加1，增加大约1ms，最大值为0xFFFF
void DelayMS(u16 count)
{
	int j;
	while (count--)
	{
		j = 150;
		while (j--);
	}
}

//大约 20um,每增加1，增加大约3um，最大值为255(0xFF)
//10----------48um
//100---------315um
void DelayUS(u8 count)
{
	while (count--);
}

/***************************************************/
u8 ViewData1[16] = { "1234567890123456" };
u8 ViewData2[16] = { "ABCDEFGHIJKLMNOP" };

u8 ViewData3[16] = { "V: 10.0000000 mV" };
u8 ViewData4[16] = { "R:  9.9999999 Ko" };

/**********************************************************/

/**********************************************************/
void main(void)
{
	u8 i;
	u16 v;
	CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV8);
	GPIO_Configuration();
	Lcd_Configuration();

	while (1)
	{
		Set_Lcd_Postion(0, 0); //设置显示位置为第一行内容
		for (i = 0; i < 16; i++)
		{
			Lcd_Write_Data(DATA, ViewData1[i]);
		}

		Set_Lcd_Postion(0, 1); //设置显示位置为第二行内容
		for (i = 0; i < 16; i++)
		{
			Lcd_Write_Data(DATA, ViewData2[i]);
		}

		DelayMS(1500);

		Set_Lcd_Postion(0, 0); //设置显示位置为第一行内容
		for (i = 0; i < 16; i++)
		{
			Lcd_Write_Data(DATA, ViewData3[i]);
		}

		Set_Lcd_Postion(0, 1); //设置显示位置为第二行内容
		for (i = 0; i < 16; i++)
		{
			Lcd_Write_Data(DATA, ViewData4[i]);
		}

		DelayMS(2000);

		for (v = 1000; v < 2999; v++)//测试快速递增的数字的显示
		{
			Set_Lcd_Postion(9, 0); 
			char* chars = tr_4(v);
			for (u8 j = 0; j < 4; j++)
			{
				Lcd_Write_Data(DATA, chars[j]);
			}
		}

		for (v = 9999; v > 7999; v--)//测试快速递增的数字的显示
		{
			Set_Lcd_Postion(9, 1); 
			char* chars = tr_4(v);
			for (u8 j = 0; j < 4; j++)
			{
				Lcd_Write_Data(DATA, chars[j]);
			}
		}

		DelayMS(1000);
	}
}
unsigned char strTemp4[4];
unsigned char* tr_4(unsigned int data)
{
	strTemp4[0] = (unsigned char)(((data / 1000) % 10) + 0x30);
	strTemp4[1] = (unsigned char)(((data / 100) % 10) + 0x30);
	strTemp4[2] = (unsigned char)(((data / 10) % 10) + 0x30);
	strTemp4[3] = (unsigned char)((data % 10) + 0x30);
	return strTemp4;
}

/********************将光标指到LCD行列位****************************/

//参数：y:行 0或1
//      x:列，0~15
void Set_Lcd_Postion(u8 x, u8 y)
{
	u8 pos;
	if (y == 0)
		pos = 0x80 + x;
	else
		pos = 0xC0 + x;
	Lcd_Write_Data(CMD, pos);
}

/****************************时钟切换*************************************/
ErrorStatus CLK_Configuration(void)
{
	ErrorStatus clk_return_status;
	FlagStatus flags_tatus;
	CLK_DeInit();
	//CLK_ClockSwitchCmd(ENABLE);
	CLK_HSECmd(ENABLE);
	do
	{
		flags_tatus = CLK_GetFlagStatus(CLK_FLAG_HSERDY);
	}
	while (flags_tatus != SET); //等待HSE稳定
	clk_return_status = CLK_ClockSwitchConfig(CLK_SWITCHMODE_AUTO, CLK_SOURCE_HSE, DISABLE, CLK_CURRENTCLOCKSTATE_DISABLE);
	return(clk_return_status); //自动切换，返回结果
}

/***********************************************************************/
void GPIO_Configuration(void)
{ //将于LCD连接IO口配置为输出模式
	GPIO_Init(Lcd_Port, Lcd_Rs | Lcd_Rw | Lcd_En, GPIO_MODE_OUT_PP_HIGH_FAST);
	GPIO_Init(GPIOD, GPIO_PIN_3 | GPIO_PIN_2, GPIO_MODE_OUT_PP_HIGH_FAST);
	GPIO_Init(GPIOC, GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6, GPIO_MODE_OUT_PP_HIGH_FAST);
	GPIO_Init(GPIOB, GPIO_PIN_4 | GPIO_PIN_5, GPIO_MODE_OUT_PP_HIGH_FAST);
}

/***********************************************************************/

u8 LCD_BUSY2(void)
{
	u8 busy = 0xff;
	GPIO_Init(GPIOD, GPIO_PIN_3 | GPIO_PIN_2, GPIO_MODE_IN_PU_NO_IT);
	GPIO_Init(GPIOC, GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6, GPIO_MODE_IN_PU_NO_IT);
	GPIO_Init(GPIOB, GPIO_PIN_4 | GPIO_PIN_5, GPIO_MODE_IN_PU_NO_IT);

	GPIO_WriteLow(Lcd_Port, Lcd_Rs);
	GPIO_WriteHigh(Lcd_Port, Lcd_Rw);
	GPIO_WriteHigh(Lcd_Port, Lcd_En);

	DelayUS(40); //最少需要650um (40x20=800)
	//busy = GPIO_ReadInputData(PortDATA);
	busy = GPIO_ReadInputPin(GPIOD, GPIO_PIN_3);
	GPIO_WriteLow(Lcd_Port, Lcd_En);
	GPIO_Init(GPIOD, GPIO_PIN_3 | GPIO_PIN_2, GPIO_MODE_OUT_PP_HIGH_FAST);
	GPIO_Init(GPIOC, GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6, GPIO_MODE_OUT_PP_HIGH_FAST);
	GPIO_Init(GPIOB, GPIO_PIN_4 | GPIO_PIN_5, GPIO_MODE_OUT_PP_HIGH_FAST);
	if (busy)
		return (1);
	else
		return (0);
}

/*****************************
	向   LCD   写   数   据
****************************************/
void Lcd_Write_Data(Data_Flag Flag, u8 data)
{
	while (LCD_BUSY2() == 1); //是命令还是数据
	if (Flag == CMD)
	{
		GPIO_WriteLow(Lcd_Port, Lcd_Rs);
	}
	else
	{
		GPIO_WriteHigh(Lcd_Port, Lcd_Rs);
	}
	GPIO_WriteLow(Lcd_Port, Lcd_Rw);
	DelayUS(2);
	GPIO_WriteLow(Lcd_Port, Lcd_En);
	DelayUS(2);
	Lcd_Write_Port_Data(data); //输出数据
	DelayUS(5);
	GPIO_WriteHigh(Lcd_Port, Lcd_En); //使能信号产生
	DelayUS(2);
	GPIO_WriteLow(Lcd_Port, Lcd_En);
	DelayUS(2);
}

void Lcd_Write_Port_Data(u8 data)
{
	if (data & 0x80)GPIO_WriteHigh(GPIOD, GPIO_PIN_3); else GPIO_WriteLow(GPIOD, GPIO_PIN_3);
	if (data & 0x40)GPIO_WriteHigh(GPIOD, GPIO_PIN_2); else GPIO_WriteLow(GPIOD, GPIO_PIN_2);
	if (data & 0x20)GPIO_WriteHigh(GPIOC, GPIO_PIN_6); else GPIO_WriteLow(GPIOC, GPIO_PIN_6);
	if (data & 0x10)GPIO_WriteHigh(GPIOC, GPIO_PIN_5); else GPIO_WriteLow(GPIOC, GPIO_PIN_5);
	if (data & 0x08)GPIO_WriteHigh(GPIOC, GPIO_PIN_4); else GPIO_WriteLow(GPIOC, GPIO_PIN_4);
	if (data & 0x04)GPIO_WriteHigh(GPIOC, GPIO_PIN_3); else GPIO_WriteLow(GPIOC, GPIO_PIN_3);
	if (data & 0x02)GPIO_WriteHigh(GPIOB, GPIO_PIN_4); else GPIO_WriteLow(GPIOB, GPIO_PIN_4);
	if (data & 0x01)GPIO_WriteHigh(GPIOB, GPIO_PIN_5); else GPIO_WriteLow(GPIOB, GPIO_PIN_5);
}

/**********************************************************************/
void Lcd_Configuration()
{
	/**************复位LCD***************/
	DelayMS(50);
	GPIO_WriteHigh(Lcd_Port, Lcd_Rs);
	DelayMS(2);
	GPIO_WriteHigh(Lcd_Port, Lcd_Rw);
	DelayMS(2);
	Lcd_Write_Data(CMD, 0x38);
	DelayMS(2);
	Lcd_Write_Data(CMD, 0x38);
	DelayMS(1);
	Lcd_Write_Data(CMD, 0x38);
	DelayMS(1);
	Lcd_Write_Data(CMD, Lcd_Clear); //清除LCD的显示内容
	DelayMS(2);
	Lcd_Write_Data(CMD, 0x06);
	DelayMS(1);
	Lcd_Write_Data(CMD, Lcd_Close_Cursor);
	DelayMS(1);
}

/*******************************************************************/

#ifdef USE_FULL_ASSERT

/**
* @brief  Reports the name of the source file and the source line number
*   where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval : None
*/
void assert_failed(u8* file, u32 line)
{
	/* User can add his own implementation to report the file name and line number,
	ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */

	/* Infinite loop */
	while (1)
	{
	}
}
#endif
